/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ola_coap_net.h"
#include <stddef.h>
#include "ola_coap_conf.h"
#include "ola_log.h"
#include "ola_error.h"
#include "ola_coap_udp.h"
#include "ola_coap_tls.h"
#include "ola_coap.h"
#include "securec.h"
#include "ola_pal_os.h"
#include "ola_base_utils.h"

static OlaCoapContext g_coapContext[OLA_COAP_CONTEXT_MAX_NUM];
static unsigned int g_recvBufSize;
static unsigned int g_sendBufSize;

static OlaCoapFrameType GetCoapFrameType(const OlaCoapContext *ctx)
{
    if (ctx->protocol == OLA_COAP_PROTOCOL_TYPE_UDP) {
        return OLA_COAP_FRAME_TYPE_UNRELIABLE;
    }
    return  OLA_COAP_FRAME_TYPE_RELIABLE;
}

int OlaCoapInit(unsigned int recvBufSize, unsigned int sendBufSize)
{
    (void)memset_s(g_coapContext, sizeof(g_coapContext), 0, sizeof(g_coapContext));
    if (recvBufSize < OLA_COAP_BUFFER_MIN_SIZE) {
        g_recvBufSize = OLA_COAP_BUFFER_MIN_SIZE;
    } else if (recvBufSize > OLA_COAP_BUFFER_MAX_SIZE) {
        g_recvBufSize = OLA_COAP_BUFFER_MAX_SIZE;
    } else {
        g_recvBufSize = recvBufSize;
    }

    if (sendBufSize < OLA_COAP_BUFFER_MIN_SIZE) {
        g_sendBufSize = OLA_COAP_BUFFER_MIN_SIZE;
    } else if (sendBufSize > OLA_COAP_BUFFER_MAX_SIZE) {
        g_sendBufSize = OLA_COAP_BUFFER_MAX_SIZE;
    } else {
        g_sendBufSize = sendBufSize;
    }

    return OLA_OK;
}

int OlaCoapDeinit(void)
{
    (void)memset_s(g_coapContext, sizeof(g_coapContext), 0, sizeof(g_coapContext));
    g_sendBufSize = 0;
    g_recvBufSize = 0;

    return OLA_OK;
}

static int CoapContextProtocolInit(OlaCoapContext *ctx, const char *url, unsigned short port)
{
    int ret = OLA_COM_ERR_NOT_SUPPORT;
    switch (ctx->protocol) {
        case OLA_COAP_PROTOCOL_TYPE_UDP:
            ret = OlaUdpInit(ctx, url, port);
            break;
        case OLA_COAP_PROTOCOL_TYPE_TLS:
            ret = OlaTlsInit(ctx, url, port);
            break;
        default:
            OlaLogWarning("invalid protocol type");
            break;
    }
    return ret;
}

static void CoapContextProtocolDeinit(OlaCoapContext *ctx)
{
    switch (ctx->protocol) {
        case OLA_COAP_PROTOCOL_TYPE_UDP:
            OlaUdpDeinit(ctx);
            break;
        case OLA_COAP_PROTOCOL_TYPE_TLS:
            OlaTlsDeinit(ctx);
            break;
        default:
            break;
    }
    return;
}

static int CoapProtocolSend(OlaCoapSession *session, const OlaData *payload)
{
    int ret = OLA_COM_ERR_NOT_SUPPORT;
    switch (session->context->protocol) {
        case OLA_COAP_PROTOCOL_TYPE_UDP:
            ret = OlaUdpSend(session, payload);
            break;
        case OLA_COAP_PROTOCOL_TYPE_TLS:
            ret = OlaTlsSend(session, payload);
        default:
            break;
    }
    return ret;
}

static int CoapProtocolRecieve(OlaCoapContext *ctx, OlaCoapSession **session, OlaBuffer *buf)
{
    int ret = OLA_COM_ERR_NOT_SUPPORT;
    switch (ctx->protocol) {
        case OLA_COAP_PROTOCOL_TYPE_UDP:
            *session = OlaUdpRead(ctx, buf);
            ret = OLA_OK;
            break;
        case OLA_COAP_PROTOCOL_TYPE_TLS:
            *session = OlaTlsRead(ctx, buf);
            ret = OLA_OK;
            break;
        default:
            break;
    }
    return ret;
}

static int CoapContextCommonInit(OlaCoapContext *ctx)
{
    ctx->mutex = OlaMutexCreate();
    if (ctx->mutex == NULL) {
        OlaLogError("mutex create error");
        return OLA_OS_ERR_CREATE_MUTEX;
    }
    ctx->errFlag = false;
    ctx->useFlag = true;
    return OLA_OK;
}

static void CoapContextCommonDeinit(OlaCoapContext *ctx)
{
    if (ctx->mutex != NULL) {
        OlaMutexUnLock(ctx->mutex);
        OlaMutexDestroy(ctx->mutex);
    }
    (void)memset_s(ctx, sizeof(OlaCoapContext), 0, sizeof(OlaCoapContext));
}

static int CoapContextUdpBufferInit(OlaCoapContext *ctx)
{
    ctx->recvBuf.buf = (unsigned char *)OlaMalloc(g_recvBufSize);
    if (ctx->recvBuf.buf == NULL) {
        OlaLogError("malloc error");
        return OLA_OS_ERR_MALLOC;
    }
    ctx->recvBuf.size = g_recvBufSize;
    /* udp涉及重发，不分配公用的缓冲区，仅使用size计数 */
    ctx->sendBuf.size = g_sendBufSize;
    return OLA_OK;
}

static void CoapContextTcpBufferDeinit(OlaCoapContext *ctx)
{
    OlaPutNullAfterFree((void **)&ctx->recvBuf.buf);
    ctx->recvBuf.size = 0;
    OlaPutNullAfterFree((void **)&ctx->sendBuf.buf);
    ctx->sendBuf.size = 0;
}

static int CoapContextTcpBufferInit(OlaCoapContext *ctx)
{
    ctx->recvBuf.buf = (unsigned char *)OlaMalloc(g_recvBufSize);
    if (ctx->recvBuf.buf == NULL) {
        OlaLogError("malloc error");
        return OLA_OS_ERR_MALLOC;
    }
    ctx->recvBuf.size = g_recvBufSize;
    ctx->sendBuf.buf = (unsigned char *)OlaMalloc(g_sendBufSize);
    if (ctx->sendBuf.buf == NULL) {
        OlaPutNullAfterFree((void **)&ctx->sendBuf.buf);
        OlaLogError("malloc error");
        return OLA_OS_ERR_MALLOC;
    }
    ctx->sendBuf.size = g_sendBufSize;
    return OLA_OK;
}

static void CoapContextUdpBufferDeinit(OlaCoapContext *ctx)
{
    OlaPutNullAfterFree((void **)&ctx->recvBuf.buf);
    ctx->recvBuf.size = 0;
}

static int CoapContextBufferInit(OlaCoapContext *ctx)
{
    int ret = OLA_COM_ERR_NOT_SUPPORT;

    switch (GetCoapFrameType(ctx)) {
        case OLA_COAP_FRAME_TYPE_RELIABLE:
            ret = CoapContextTcpBufferInit(ctx);
            break;
        case OLA_COAP_FRAME_TYPE_UNRELIABLE:
            ret = CoapContextUdpBufferInit(ctx);
            break;
        default:
            break;
    }

    return ret;
}

static void CoapContextBufferDeinit(OlaCoapContext *ctx)
{
    switch (GetCoapFrameType(ctx)) {
        case OLA_COAP_FRAME_TYPE_RELIABLE:
            CoapContextTcpBufferDeinit(ctx);
            break;
        case OLA_COAP_FRAME_TYPE_UNRELIABLE:
            CoapContextUdpBufferDeinit(ctx);
            break;
        default:
            break;
    }
    return;
}

OlaCoapContext *OlaCoapNewContext(const char *url, unsigned short port, OlaCoapProtocolType type)
{
    OlaCoapContext *ctx = NULL;
    for (unsigned int i = 0; i < OLA_COAP_CONTEXT_MAX_NUM; ++i) {
        if (!g_coapContext[i].useFlag) {
            ctx = &g_coapContext[i];
            (void)memset_s(ctx, sizeof(OlaCoapContext), 0, sizeof(OlaCoapContext));
            break;
        }
    }
    if (ctx == NULL) {
        OlaLogWarning("context over max num");
        return NULL;
    }
    ctx->protocol = type;
    ctx->type = (url == NULL) ? OLA_COAP_CONTEXT_TYPE_SERVER : OLA_COAP_CONTEXT_TYPE_CLIENT;

    int ret = CoapContextProtocolInit(ctx, url, port);
    if (ret != OLA_OK) {
        (void)memset_s(ctx, sizeof(OlaCoapContext), 0, sizeof(OlaCoapContext));
        OlaLogWarning("protocol init failed");
        return NULL;
    }
    ret = CoapContextBufferInit(ctx);
    if (ret != OLA_OK) {
        CoapContextProtocolDeinit(ctx);
        (void)memset_s(ctx, sizeof(OlaCoapContext), 0, sizeof(OlaCoapContext));
        OlaLogWarning("protocol init failed");
        return NULL;
    }

    ret = CoapContextCommonInit(ctx);
    if (ret != OLA_OK) {
        CoapContextProtocolDeinit(ctx);
        CoapContextBufferDeinit(ctx);
        (void)memset_s(ctx, sizeof(OlaCoapContext), 0, sizeof(OlaCoapContext));
        OlaLogWarning("protocol init failed");
        return NULL;
    }

    return ctx;
}

/* 仅需要在异常场景以及释放重发节点时调用 */
static void CoapReleaseSendBuf(OlaCoapContext *ctx, OlaBuffer *buf)
{
    switch (GetCoapFrameType(ctx)) {
        case OLA_COAP_FRAME_TYPE_RELIABLE:
            buf->buf = NULL;
            buf->size = 0;
            break;
        case OLA_COAP_FRAME_TYPE_UNRELIABLE:
            OlaPutNullAfterFree((void **)&buf->buf);
            if (ctx->sendBuf.len >= buf->size) {
                ctx->sendBuf.len -= buf->size;
            } else {
                OlaLogWarning("send buf len error");
                ctx->sendBuf.len = 0;
            }
            buf->size = 0;
            break;
        default:
            break;
    }
}

static void CoapReleaseSession(OlaCoapSession *session)
{
    switch (session->context->protocol) {
        case OLA_COAP_PROTOCOL_TYPE_UDP:
            OlaUdpSessionRelease(session);
            break;
        case OLA_COAP_PROTOCOL_TYPE_TLS:
            OlaTlsSessionRelease(session);
            break;
        default:
            break;
    }
}

static void CoapContextSessListReleaseItem(OlaCoapContext *ctx, OlaCoapSession *item)
{
    OlaCoapSession head;
    (void)memset_s(&head, sizeof(OlaCoapSession), 0, sizeof(OlaCoapSession));
    head.next = ctx->session;
    OlaCoapSession *pre = &head;
    OlaCoapSession *cur = head.next;
    while (cur != NULL) {
        if (cur == item) {
            pre->next = cur->next;
            CoapReleaseSession(cur);
            cur = pre->next;
            continue;
        }
        pre = cur;
        cur = cur->next;
    }
    ctx->session = head.next;
}

static int CoapContextSessListCheck(OlaCoapContext *ctx)
{
    int ret = OLA_OK;
    OlaCoapSession head;
    (void)memset_s(&head, sizeof(OlaCoapSession), 0, sizeof(OlaCoapSession));
    head.next = ctx->session;
    OlaCoapSession *pre = &head;
    OlaCoapSession *cur = ctx->session;
    bool isOverTime = false;
    while (cur != NULL) {
        ret = OlaIsOvertime(&isOverTime, cur->lastRecvTime, OLA_COAP_SESSION_TIMEOUT);
        if (ret != OLA_OK) {
            OlaLogError("overtime check error");
            break;
        }

        if (isOverTime) {
            OlaLogDebug("remove overtime session");
            pre->next = cur->next;
            CoapReleaseSession(cur);
            cur = pre->next;
        } else {
            pre = cur;
            cur = cur->next;
        }
    }
    ctx->session = head.next;
    return ret;
}

static void CoapContextSessListClear(OlaCoapContext *ctx)
{
    if (ctx->session == NULL) {
        return;
    }
    OlaCoapSession *cur = ctx->session;
    OlaCoapSession *next = NULL;
    while (cur != NULL) {
        next = (OlaCoapSession *)cur->next;
        CoapReleaseSession(cur);
        cur = next;
    }
}

static void CoapContextResListClear(OlaCoapContext *ctx)
{
    if (ctx->resource == NULL) {
        return;
    }
    OlaCoapResource *cur = ctx->resource;
    OlaCoapResource *next = NULL;
    while (cur != NULL) {
        next = (OlaCoapResource *)cur->next;
        OlaPutNullAfterFree((void **)&cur);
        cur = next;
    }
}

static void CoapContextResendListReleaseItem(OlaCoapContext *ctx, OlaCoapResendItem *item)
{
    OlaCoapResendItem head;
    (void)memset_s(&head, sizeof(OlaCoapResendItem), 0, sizeof(OlaCoapResendItem));
    head.next = ctx->resendMsg;
    OlaCoapResendItem *pre = &head;
    OlaCoapResendItem *cur = head.next;
    while (cur != NULL) {
        if (cur == item) {
            pre->next = cur->next;
            CoapReleaseSendBuf(ctx, &cur->buf);
            OlaPutNullAfterFree((void **)&cur);
            cur = pre->next;
            continue;
        }
        pre = cur;
        cur = cur->next;
    }
    ctx->resendMsg = head.next;
}

static void CoapContextResendListAddItem(OlaCoapContext *ctx, OlaCoapResendItem *item)
{
    if (ctx->resendMsg == NULL) {
        ctx->resendMsg = item;
        return;
    }
    OlaCoapResendItem *cur = ctx->resendMsg;
    unsigned int count = 1;
    while (cur->next != NULL) {
        ++count;
        /* 链表超长后移除头结点 */
        if (count >= OLA_COAP_CONTEXT_RESEND_ITEM_MAX_NUM) {
            CoapContextResendListReleaseItem(ctx, ctx->resendMsg);
            --count;
        }
        cur = cur->next;
    }
    cur->next = item;
    OlaLogDebug("add resend item, count [%u]\r\n", count);
}

static OlaCoapResendItem *CoapResendListFindItem(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    OlaCoapResendItem *ret = NULL;
    OlaCoapResendItem *cur = session->context->resendMsg;
    while (cur != NULL) {
        if ((cur->session == session) &&
            (cur->packet.header.msgId == pkt->header.msgId) &&
            cur->packet.header.tokenLen == pkt->header.tokenLen &&
            ((cur->packet.header.tokenLen == 0) ||
            ((cur->packet.token.data != NULL) && (pkt->token.data != NULL) &&
            (memcmp(cur->packet.token.data, pkt->token.data, pkt->header.tokenLen)) == 0))) {
            ret = cur;
            break;
        }
        cur = cur->next;
    }
    return ret;
}

static void CoapContextResendListClear(OlaCoapContext *ctx)
{
    if (ctx->resendMsg == NULL) {
        return;
    }
    OlaCoapResendItem *cur = ctx->resendMsg;
    OlaCoapResendItem *next = NULL;
    while (cur != NULL) {
        next = cur->next;
        CoapReleaseSendBuf(ctx, &cur->buf);
        OlaPutNullAfterFree((void **)&cur);
        cur = next;
    }
}

static int CoapContextInvalidCheck(OlaCoapContext *ctx)
{
    if (ctx == NULL) {
        OlaLogWarning("invalid context");
        return OLA_COAP_ERR_INVALID_CONTEXT;
    }
    bool find = false;
    for (unsigned int i = 0; i < OLA_COAP_CONTEXT_MAX_NUM; ++i) {
        if (ctx == &g_coapContext[i]) {
            find = true;
        }
    }
    if (!find) {
        OlaLogWarning("invalid context");
        return OLA_COAP_ERR_INVALID_CONTEXT;
    }
    return OLA_OK;
}

static int CoapSessionInvalidCheck(OlaCoapSession *session)
{
    int ret;
    OlaCoapContext *ctx = session->context;
    if (ctx == NULL) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    ret = CoapContextInvalidCheck(ctx);
    if (ret != OLA_OK) {
        OlaLogWarning("session has invalid context");
        return ret;
    }

    if (!ctx->useFlag || ctx->errFlag || (ctx->fd < 0)) {
        OlaLogWarning("invalid context");
        return OLA_COAP_ERR_INVALID_CONTEXT;
    }

    bool find = false;
    OlaCoapSession *cur = session->context->session;
    while (cur != NULL) {
        if (cur == session) {
            find = true;
            break;
        }
        cur = cur->next;
    }
    if (!find) {
        OlaLogWarning("invalid session");
        return OLA_COAP_ERR_INVALID_SESSION;
    }
    return OLA_OK;
}

/* 返回OLA_ERRO表示已达最大重发次数或不满足重发要求 */
static int CoapResendItemCheck(const OlaCoapResendItem *item, bool *isResend)
{
    int ret = CoapSessionInvalidCheck(item->session);
    if (ret != OLA_OK) {
        return ret;
    }
    if (item->resendFlag == OLA_COAP_RESEND_TYPE_FIRST_MSG) {
        if (item->sendCount > OLA_COAP_UDP_RESEND_FIRST_TIMES) {
            return OLA_COAP_ERR_RESEND_OVER_MAX_NUM;
        }
        ret = OlaIsOvertime(isResend, item->lastSendTime, OLA_COAP_UDP_RESEND_FIRST_TIMEVEL);
        if (ret != OLA_OK) {
            OlaLogError("overtime check error");
            return ret;
        }
    } else if (item->resendFlag == OLA_COAP_RESEND_TYPE_LAST_MSG) {
        if (item->sendCount > OLA_COAP_UDP_RESEND_LAST_TIMES) {
            return OLA_COAP_ERR_RESEND_OVER_MAX_NUM;
        }
        ret = OlaIsOvertime(isResend, item->lastSendTime, OLA_COAP_UDP_RESEND_LAST_TIMEVEL);
        if (ret != OLA_OK) {
            OlaLogError("overtime check error");
            return OLA_ERROR;
        }
    } else if (item->resendFlag == OLA_COAP_RESEND_TYPE_OTHER) {
        if (item->sendCount > OLA_COAP_UDP_RESEND_MID_TIMES) {
            return OLA_COAP_ERR_RESEND_OVER_MAX_NUM;
        }
        ret = OlaIsOvertime(isResend, item->lastSendTime, OLA_COAP_UDP_RESEND_MID_TIMEVEL);
        if (ret != OLA_OK) {
            OlaLogError("overtime check error");
            return ret;
        }
    } else {
        return OLA_COM_ERR_NOT_SUPPORT;
    }

    return OLA_OK;
}

static int CoapContextResendListCheck(OlaCoapContext *ctx)
{
    int ret = OLA_OK;
    OlaCoapResendItem head;
    (void)memset_s(&head, sizeof(OlaCoapResendItem), 0, sizeof(OlaCoapResendItem));
    head.next = ctx->resendMsg;
    OlaCoapResendItem *cur = ctx->resendMsg;
    OlaCoapResendItem *pre = &head;
    bool isResend = false;
    while (cur != NULL) {
        ret = CoapResendItemCheck(cur, &isResend);
        if (ret != OLA_OK) {
            pre->next = cur->next;
            CoapReleaseSendBuf(ctx, &cur->buf);
            /* udp是无连接的，重发完成即可释放会话 */
            CoapContextSessListReleaseItem(ctx, cur->session);
            OlaPutNullAfterFree((void **)&cur);
            cur = pre->next;
            continue;
        }

        if (isResend) {
            OlaLogDebug("resend item type[%u] msgid[%X] sendcount[%u]", cur->resendFlag,
                cur->packet.header.msgId, cur->sendCount);
            OlaData payload = {cur->buf.buf, cur->buf.len};
            ret = CoapProtocolSend(cur->session, &payload);
            if (ret != OLA_OK || OlaGetTime(&cur->lastSendTime) != 0) {
                pre->next = cur->next;
                CoapReleaseSendBuf(ctx, &cur->buf);
                CoapContextSessListReleaseItem(ctx, cur->session);
                OlaPutNullAfterFree((void **)&cur);
                cur = pre->next;
                continue;
            }
            ++cur->sendCount;
        }
        pre = cur;
        cur = cur->next;
    }
    ctx->resendMsg = head.next;
    return OLA_OK;
}

static int CoapContextReqListCheck(OlaCoapContext *ctx)
{
    int ret = OLA_OK;
    OlaCoapReqItem head;
    (void)memset_s(&head, sizeof(OlaCoapReqItem), 0, sizeof(OlaCoapReqItem));
    head.next = ctx->reqMsg;
    OlaCoapReqItem *cur = ctx->reqMsg;
    OlaCoapReqItem *pre = &head;
    bool isOverTime = false;
    while (cur != NULL) {
        ret = OlaIsOvertime(&isOverTime, cur->reqTime, OLA_COAP_REQUEST_TIMEOUT);
        if (ret != OLA_OK) {
            OlaLogError("overtime check error");
            break;
        }

        if (isOverTime) {
            OlaLogDebug("remove overtime req item");
            pre->next = cur->next;
            OlaPutNullAfterFree((void **)&cur);
            cur = pre->next;
        } else {
            pre = cur;
            cur = cur->next;
        }
    }
    ctx->reqMsg = head.next;
    return ret;
}

static void CoapContextReqListReleaseItem(OlaCoapContext *ctx, OlaCoapReqItem *item)
{
    OlaCoapReqItem head;
    (void)memset_s(&head, sizeof(OlaCoapReqItem), 0, sizeof(OlaCoapReqItem));
    head.next = ctx->reqMsg;
    OlaCoapReqItem *pre = &head;
    OlaCoapReqItem *cur = head.next;
    while (cur != NULL) {
        if (cur == item) {
            pre->next = cur->next;
            OlaPutNullAfterFree((void **)&cur);
            cur = pre->next;
            continue;
        }
        pre = cur;
        cur = cur->next;
    }
    ctx->reqMsg = head.next;
}

static void CoapContextReqListAddItem(OlaCoapContext *ctx, OlaCoapReqItem *item)
{
    if (ctx->reqMsg == NULL) {
        ctx->reqMsg = item;
        return;
    }
    OlaCoapReqItem *cur = ctx->reqMsg;
    unsigned int count = 1;
    while (cur->next != NULL) {
        ++count;
        /* 链表超长后移除头结点 */
        if (count >= OLA_COAP_CONTEXT_REQ_ITEM_MAX_NUM) {
            CoapContextReqListReleaseItem(ctx, ctx->reqMsg);
            --count;
        }
        cur = cur->next;
    }
    cur->next = item;
}

static void CoapContextReqListClear(OlaCoapContext *ctx)
{
    if (ctx->reqMsg == NULL) {
        return;
    }
    OlaCoapReqItem *cur = ctx->reqMsg;
    OlaCoapReqItem *next = NULL;
    while (cur != NULL) {
        next = cur->next;
        OlaPutNullAfterFree((void **)&cur);
        cur = next;
    }
}

static OlaCoapReqItem *CoapReqListFindItem(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    OlaCoapReqItem *ret = NULL;
    OlaCoapReqItem *cur = session->context->reqMsg;
    while (cur != NULL) {
        if ((cur->session == session) &&
            (cur->msgId == pkt->header.msgId) &&
            (cur->tokenLen == pkt->header.tokenLen) &&
            ((cur->tokenLen == 0) || ((pkt->token.data != NULL) &&
            (memcmp(cur->token, pkt->token.data, cur->tokenLen) == 0)))) {
            ret = cur;
            break;
        }
        cur = cur->next;
    }
    return ret;
}

static OlaCoapRecvItem *CoapRecvListFindItemByReqid(OlaCoapSession *session, OlaReqId reqId)
{
    OlaCoapRecvItem *ret = NULL;
    OlaCoapRecvItem *cur = session->context->recvMsg;
    while (cur != NULL) {
        /* reqid为节点指针 */
        if (((OlaReqId)cur == reqId) && (cur->session == session)) {
            ret = cur;
            break;
        }
        cur = cur->next;
    }
    return ret;
}

static OlaCoapRecvItem *CoapRecvListFindItemByPkt(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    OlaCoapRecvItem *ret = NULL;
    OlaCoapRecvItem *cur = session->context->recvMsg;
    while (cur != NULL) {
        if ((cur->session == session) &&
            (cur->msgId == pkt->header.msgId) &&
            (cur->tokenLen == pkt->header.tokenLen) &&
            ((cur->tokenLen == 0) || ((pkt->token.data != NULL) &&
            (memcmp(cur->token, pkt->token.data, cur->tokenLen) == 0)))) {
            ret = cur;
            break;
        }
        cur = cur->next;
    }
    return ret;
}

static int CoapContextRecvListCheck(OlaCoapContext *ctx)
{
    int ret = OLA_OK;
    OlaCoapRecvItem head;
    (void)memset_s(&head, sizeof(OlaCoapRecvItem), 0, sizeof(OlaCoapRecvItem));
    head.next = ctx->recvMsg;
    OlaCoapRecvItem *cur = ctx->recvMsg;
    OlaCoapRecvItem *pre = &head;
    bool isOverTime = false;
    while (cur != NULL) {
        ret = OlaIsOvertime(&isOverTime, cur->recvTime, OLA_COAP_RECV_TIMEOUT);
        if (ret != OLA_OK) {
            OlaLogError("overtime check error");
            break;
        }

        if (isOverTime) {
            OlaLogDebug("remove overtime recv item");
            pre->next = cur->next;
            OlaPutNullAfterFree((void **)&cur);
            cur = pre->next;
        } else {
            pre = cur;
            cur = cur->next;
        }
    }
    ctx->recvMsg = head.next;
    return ret;
}

static void CoapContextRecvListReleaseItem(OlaCoapContext *ctx, OlaCoapRecvItem *item)
{
    OlaCoapRecvItem head;
    (void)memset_s(&head, sizeof(OlaCoapRecvItem), 0, sizeof(OlaCoapRecvItem));
    head.next = ctx->recvMsg;
    OlaCoapRecvItem *pre = &head;
    OlaCoapRecvItem *cur = head.next;
    while (cur != NULL) {
        if (cur == item) {
            pre->next = cur->next;
            OlaPutNullAfterFree((void **)&cur);
            cur = pre->next;
            continue;
        }
        pre = cur;
        cur = cur->next;
    }
    ctx->recvMsg = head.next;
}

static void CoapContextRecvListAddItem(OlaCoapContext *ctx, OlaCoapRecvItem *item)
{
    if (ctx->recvMsg == NULL) {
        ctx->recvMsg = item;
        return;
    }
    OlaCoapRecvItem *cur = ctx->recvMsg;
    unsigned int count = 1;
    while (cur->next != NULL) {
        ++count;
        /* 链表超长后移除头结点 */
        if (count >= OLA_COAP_CONTEXT_RECV_ITEM_MAX_NUM) {
            CoapContextRecvListReleaseItem(ctx, ctx->recvMsg);
            --count;
        }
        cur = cur->next;
    }
    cur->next = item;
}

static void CoapContextRecvListClear(OlaCoapContext *ctx)
{
    if (ctx->recvMsg == NULL) {
        return;
    }
    OlaCoapRecvItem *cur = ctx->recvMsg;
    OlaCoapRecvItem *next = NULL;
    while (cur != NULL) {
        next = cur->next;
        OlaPutNullAfterFree((void **)&cur);
        cur = next;
    }
}

static void CoapConetxtListRelease(OlaCoapContext *ctx)
{
    CoapContextSessListClear(ctx);
    CoapContextResListClear(ctx);
    CoapContextResendListClear(ctx);
    CoapContextReqListClear(ctx);
    CoapContextRecvListClear(ctx);
}

int OlaCoapFreeContext(OlaCoapContext *ctx)
{
    int ret = CoapContextInvalidCheck(ctx);
    if (ret != OLA_OK) {
        OlaLogWarning("invalid context");
        return ret;
    }
    ret = OlaMutexLock(ctx->mutex, OLA_COAP_MUTEX_TIMEOUT);
    if (ret != 0) {
        OlaLogError("lock error[%d]", ret);
        return OLA_OS_ERR_MUTEX_LOCK;
    }
    CoapConetxtListRelease(ctx);
    CoapContextProtocolDeinit(ctx);
    CoapContextBufferDeinit(ctx);
    CoapContextCommonDeinit(ctx);
    return OLA_OK;
}

static int CoapBuildReqCoapPktParam(OlaCoapSession *session, const OlaData *data,
    const OlaCoapParam *param, OlaCoapPacketParam *pktParam)
{
    pktParam->frameType = GetCoapFrameType(session->context);
    pktParam->msgType = OLA_COAP_MSG_TYPE_CON;
    pktParam->payload.data = data->data;
    pktParam->payload.len = data->len;
    if (memcpy_s(pktParam->token, sizeof(pktParam->token), &session->token, sizeof(session->token)) != EOK) {
        OlaLogError("memcpy error");
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    pktParam->tokenLen = sizeof(session->token);
    pktParam->code = param->method;
    pktParam->msgId = session->sendMsgId;
    pktParam->optionsNum = param->optionsNum;
    if (param->optionsNum > OLA_COAP_OPTION_MAX_NUM) {
        OlaLogWarning("options num invalid");
        return OLA_COAP_ERR_INVALID_ARGUMENT;
    }
    if (memcpy_s(pktParam->options, OLA_COAP_OPTION_MAX_NUM * sizeof(OlaCoapOption),
        param->options, param->optionsNum * sizeof(OlaCoapOption)) != EOK) {
        OlaLogError("memcpy error");
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    return OLA_OK;
}

static int CoapBuildAsynRespCoapPktParam(OlaCoapSession *session, const OlaData *data,
    const OlaCoapParam *param, OlaCoapPacketParam *pktParam)
{
    OlaCoapRecvItem *item = CoapRecvListFindItemByReqid(session, param->reqId);
    if (item == NULL) {
        OlaLogWarning("not find asyn item");
        return OLA_COAP_ERR_FIND_ASYN_ITEM;
    }
    pktParam->frameType = GetCoapFrameType(session->context);
    pktParam->msgType = OLA_COAP_MSG_TYPE_ACK;
    pktParam->code = OLA_COAP_RESPONSE_CODE_CONTENT;
    pktParam->msgId = item->msgId;
    pktParam->optionsNum = 0;
    pktParam->tokenLen = item->tokenLen;
    if (item->tokenLen != 0) {
        if (memcpy_s(pktParam->token, OLA_COAP_TOKEN_MAX_LEN, item->token, item->tokenLen) != EOK) {
            OlaLogError("memcpy error");
            return OLA_SECUREC_ERR_MEMCOPY;
        }
    }
    pktParam->payload.data = data->data;
    pktParam->payload.len = data->len;
    return OLA_OK;
}

static int CoapBuildSendDataPktParam(OlaCoapSession *session, const OlaData *data,
    const OlaCoapParam *param, OlaCoapPacketParam *pktParam)
{
    int ret = OLA_COM_ERR_NOT_SUPPORT;
    switch (param->type) {
        case OLA_COAP_PACKET_TYPE_REQUEST:
            ret = CoapBuildReqCoapPktParam(session, data, param, pktParam);
            break;
        case OLA_COAP_PACKET_TYPE_RESPONSE:
            ret = CoapBuildAsynRespCoapPktParam(session, data, param, pktParam);
            break;
        default:
            break;
    }
    return ret;
}

static int CoapGetSendBuf(OlaCoapContext *ctx, const OlaCoapPacketParam *param, OlaBuffer *buf)
{
    unsigned int size;
    int ret = OlaCoapGetSize(param, &size);
    if ((ret != OLA_OK) || (size > ctx->sendBuf.size)) {
        OlaLogWarning("get size error[%d]", ret);
        return OLA_COAP_ERR_BUFFER_NOT_ENOUGH;
    }
    /* 非UDP使用公用缓冲区，UDP分配内存并计数 */
    switch (GetCoapFrameType(ctx)) {
        case OLA_COAP_FRAME_TYPE_RELIABLE:
            buf->buf = ctx->sendBuf.buf;
            buf->size = ctx->sendBuf.size;
            break;
        case OLA_COAP_FRAME_TYPE_UNRELIABLE:
            if ((ctx->sendBuf.size < ctx->sendBuf.len) ||
                (size > (ctx->sendBuf.size - ctx->sendBuf.len))) {
                OlaLogWarning("buffer is not enough to send msg");
                return OLA_COAP_ERR_BUFFER_NOT_ENOUGH;
            }
            buf->buf = (unsigned char *)OlaMalloc(size);
            if (buf->buf == NULL) {
                OlaLogError("malloc error");
                return OLA_OS_ERR_MALLOC;
            }
            ctx->sendBuf.len += size;
            buf->size = size;
            break;
        default:
            break;
    }
    return OLA_OK;
}

static OlaCoapReqItem *CoapBuildReqItem(OlaCoapSession *session,
    const OlaCoapParam *param, const OlaCoapPacket *packet)
{
    OlaCoapReqItem *item = (OlaCoapReqItem *)OlaMalloc(sizeof(OlaCoapReqItem));
    if (item == NULL) {
        OlaLogError("malloc error");
        return NULL;
    }
    (void)memset_s(item, sizeof(OlaCoapReqItem), 0, sizeof(OlaCoapReqItem));
    item->msgId = packet->header.msgId;
    item->tokenLen = packet->token.len;
    if (item->tokenLen != 0) {
        if (memcpy_s(item->token, OLA_COAP_TOKEN_MAX_LEN, packet->token.data, packet->token.len) != EOK) {
            OlaLogError("memcpy error");
            goto FAILED;
        }
    }
    item->session = session;
    if (OlaGetTime(&item->reqTime) != 0) {
        OlaLogError("get time error");
        goto FAILED;
    }
    item->handler = param->handler;
    return item;
FAILED:
    OlaPutNullAfterFree((void **)&item);
    return NULL;
}

static int CoapAddReqMsgToContext(OlaCoapSession *session, const OlaCoapParam *param, const OlaCoapPacket *packet)
{
    /* 请求加入请求链表 */
    OlaCoapReqItem *reqItem = CoapBuildReqItem(session, param, packet);
    if (reqItem == NULL) {
        OlaLogWarning("build req item error");
        return OLA_COAP_ERR_BUILD_REQ_ITEM;
    }

    CoapContextReqListAddItem(session->context, reqItem);
    return OLA_OK;
}

static OlaCoapResendItem *CoapBuildResendItem(OlaCoapSession *session, const OlaCoapPacket *packet,
    const OlaBuffer *buf)
{
    OlaCoapResendItem *item = (OlaCoapResendItem *)OlaMalloc(sizeof(OlaCoapResendItem));
    if (item == NULL) {
        OlaLogError("malloc error");
        return NULL;
    }
    (void)memset_s(item, sizeof(OlaCoapResendItem), 0, sizeof(OlaCoapResendItem));
    if (session->lastRecvTime == 0) {
        item->resendFlag = OLA_COAP_RESEND_TYPE_FIRST_MSG;
    } else {
        if (session->overFlag) {
            item->resendFlag = OLA_COAP_RESEND_TYPE_LAST_MSG;
        } else {
            item->resendFlag = OLA_COAP_RESEND_TYPE_OTHER;
        }
    }
    item->sendCount = 0;
    if (memcpy_s(&item->packet, sizeof(OlaCoapPacket), packet, sizeof(OlaCoapPacket)) != EOK) {
        OlaLogError("memcpy error");
        goto FAILED;
    }
    if (memcpy_s(&item->buf, sizeof(OlaBuffer), buf, sizeof(OlaBuffer)) != EOK) {
        OlaLogError("memcpy error");
        goto FAILED;
    }
    if (OlaGetTime(&item->lastSendTime) != 0) {
        OlaLogError("get time error");
        goto FAILED;
    }
    item->session = session;
    return item;
FAILED:
    OlaPutNullAfterFree((void **)&item);
    return NULL;
}

static int CoapAddResendMsgToContext(OlaCoapSession *session, const OlaCoapPacket *packet, const OlaBuffer *buf)
{
    OlaCoapResendItem *resendItem = CoapBuildResendItem(session, packet, buf);
    if (resendItem == NULL) {
        OlaLogWarning("build resend item error");
        return OLA_COAP_ERR_BUILD_RESEND_ITEM;
    }
    CoapContextResendListAddItem(session->context, resendItem);
    return OLA_OK;
}

static void CoapRefreshSessionAfterSendReq(OlaCoapSession *session)
{
    ++session->sendMsgId;
    ++session->token;
    if (OlaGetTime(&session->lastSendTime) != 0) {
        OlaLogCrit("get time error");
        return;
    }
}

static int CoapAddSendMsgToContext(OlaCoapSession *session, const OlaCoapParam *param,
    const OlaCoapPacket *packet, OlaBuffer *buf)
{
    int ret = OLA_OK;
    if ((param != NULL) && (param->type == OLA_COAP_PACKET_TYPE_REQUEST)) {
        CoapRefreshSessionAfterSendReq(session);
        ret = CoapAddReqMsgToContext(session, param, packet);
        if (ret != OLA_OK) {
            OlaLogWarning("add req item error");
            return ret;
        }
    }

    if (GetCoapFrameType(session->context) == OLA_COAP_FRAME_TYPE_UNRELIABLE) {
        ret = CoapAddResendMsgToContext(session, packet, buf);
        if (ret != OLA_OK) {
            OlaLogWarning("add resend item error");
            return ret;
        }
    }

    return ret;
}

/* param可以为空，发送request时需要携带 */
static int CoapSendDataInner(OlaCoapSession *session, const OlaCoapParam *param,
    const OlaCoapPacketParam *pktParam)
{
    int ret = OLA_OK;
    do {
        OlaBuffer buf = {NULL, 0, 0};
        ret = CoapGetSendBuf(session->context, pktParam, &buf);
        if ((ret != OLA_OK) || (buf.buf == NULL) || (buf.size == 0)) {
            OlaLogWarning("get send buf error[%d]", ret);
            ret = OLA_COAP_ERR_GET_SEND_BUFFER;
            break;
        }

        OlaCoapPacket packet;
        (void)memset_s(&packet, sizeof(OlaCoapPacket), 0, sizeof(OlaCoapPacket));
        ret = OlaCoapEncode(&packet, pktParam, &buf);
        if (ret != OLA_OK) {
            OlaLogWarning("coap decode error");
            CoapReleaseSendBuf(session->context, &buf);
            break;
        }
        OlaCoapDumpPacket(&packet);
        OlaData payload = {buf.buf, buf.len};
        ret = CoapProtocolSend(session, &payload);
        if (ret != OLA_OK) {
            OlaLogWarning("coap send error");
            CoapReleaseSendBuf(session->context, &buf);
            break;
        }

        ret = CoapAddSendMsgToContext(session, param, &packet, &buf);
        if (ret != OLA_OK) {
            OlaLogWarning("coap send error");
            CoapReleaseSendBuf(session->context, &buf);
            break;
        }
    } while (0);
    return ret;
}

int OlaCoapSendData(OlaCoapSession *session, const OlaData *data, const OlaCoapParam *param)
{
    if ((session == NULL) || (param == NULL) || (data == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    int ret;
    ret = CoapSessionInvalidCheck(session);
    if (ret != OLA_OK) {
        OlaLogWarning("session invalid");
        return ret;
    }

    ret = OlaMutexLock(session->context->mutex, OLA_COAP_MUTEX_TIMEOUT);
    if (ret != 0) {
        OlaLogError("lock error[%d]", ret);
        return OLA_OS_ERR_MUTEX_LOCK;
    }
    do {
        OlaCoapPacketParam pktParam;
        memset_s(&pktParam, sizeof(OlaCoapPacketParam), 0, sizeof(OlaCoapPacketParam));
        ret = CoapBuildSendDataPktParam(session, data, param, &pktParam);
        if (ret != OLA_OK) {
            OlaLogWarning("build pkt param error");
            break;
        }

        ret = CoapSendDataInner(session, param, &pktParam);
        if (ret != OLA_OK) {
            OlaLogWarning("send data error");
            break;
        }
    } while (0);

    (void)OlaMutexUnLock(session->context->mutex);
    return ret;
}

static OlaCoapResource *CoapContextFindResource(OlaCoapContext *ctx, const OlaData *uri, const OlaCoapPacket *pkt)
{
    OlaCoapResource *ret = NULL;
    OlaCoapResource *cur = ctx->resource;
    while (cur != NULL) {
        if ((cur->method ==  pkt->header.code) &&
            (strlen((const char *)cur->uri) == uri->len) &&
            (memcmp(cur->uri, uri->data, uri->len) == 0)) {
            ret = cur;
            break;
        }
        cur = cur->next;
    }
    return ret;
}

static int CoapContextAddResource(OlaCoapContext *ctx, const OlaCoapResource *resource)
{
    if ((resource->uri[OLA_COAP_URI_MAX_LEN - 1] != 0) || (resource->handler == NULL) ||
        (resource->method > OLA_COAP_METHOD_TYPE_DELETE) ||
        (resource->next != NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }
    OlaCoapResource *node = (OlaCoapResource *)OlaMalloc(sizeof(OlaCoapResource));
    if (node == NULL) {
        OlaLogError("malloc error");
        return OLA_OS_ERR_MALLOC;
    }
    (void)memset_s(node, sizeof(OlaCoapResource), 0, sizeof(OlaCoapResource));
    if (memcpy_s(node, sizeof(OlaCoapResource), resource, sizeof(OlaCoapResource)) != EOK) {
        OlaLogError("memcpy error");
        OlaPutNullAfterFree((void **)&node);
        return OLA_SECUREC_ERR_MEMCOPY;
    }
    OlaLogDebug("add uri %s", node->uri);
    /* 插入单向链表 */
    if (ctx->resource == NULL) {
        ctx->resource = node;
    } else {
        OlaCoapResource *curNode = ctx->resource;
        while (curNode->next != NULL) {
            curNode = curNode->next;
        }
        curNode->next = node;
    }

    return OLA_OK;
}

int OlaCoapRegisterResource(OlaCoapContext *ctx, const OlaCoapResource *resource)
{
    if ((ctx == NULL) || (resource == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    int ret = CoapContextInvalidCheck(ctx);
    if (ret != OLA_OK) {
        OlaLogWarning("invalid context");
        return ret;
    }

    if (!ctx->useFlag) {
        OlaLogWarning("invalid context");
        return OLA_COAP_ERR_CONTEXT_INVALID;
    }
    ret = OlaMutexLock(ctx->mutex, OLA_COAP_MUTEX_TIMEOUT);
    if (ret != 0) {
        OlaLogError("lock error[%d]", ret);
        return OLA_OS_ERR_MUTEX_LOCK;
    }

    ret = CoapContextAddResource(ctx, resource);
    if (ret != OLA_OK) {
        (void)OlaMutexUnLock(ctx->mutex);
        OlaLogWarning("add resource failed");
        return ret;
    }
    (void)OlaMutexUnLock(ctx->mutex);
    return OLA_OK;
}

int OlaCoapRegisterEventHandler(OlaCoapContext *ctx, OlaCoapEventHandler handler)
{
    if ((ctx == NULL) || (handler == NULL)) {
        OlaLogWarning("invalid param");
        return OLA_COM_ERR_INVALID_ARGUMENT;
    }

    int ret = CoapContextInvalidCheck(ctx);
    if (ret != OLA_OK) {
        OlaLogWarning("invalid context");
        return ret;
    }

    if (!ctx->useFlag) {
        OlaLogWarning("invalid context");
        return OLA_COAP_ERR_CONTEXT_INVALID;
    }
    ret = OlaMutexLock(ctx->mutex, OLA_COAP_MUTEX_TIMEOUT);
    if (ret != 0) {
        OlaLogError("lock error[%d]", ret);
        return OLA_OS_ERR_MUTEX_LOCK;
    }

    ctx->eventHandler = handler;
    (void)OlaMutexUnLock(ctx->mutex);
    return OLA_OK;
}

static void CoapContextSelectErrorProcess(void)
{
    OlaSocketErrorCode err;
    for (unsigned int i = 0; i < OLA_COAP_CONTEXT_MAX_NUM; ++i) {
        if (g_coapContext[i].useFlag && !g_coapContext[i].errFlag &&
            g_coapContext[i].fd >= 0) {
            err = OlaGetSocketError(g_coapContext[i].fd);
            if (err != OLA_SOCKET_ERROR_NO_ERROR) {
                OlaLogError("socket [%d] error [%d]", g_coapContext[i].fd, err);
                g_coapContext[i].errFlag = true;
                if (g_coapContext[i].eventHandler != NULL) {
                    g_coapContext[i].eventHandler(&g_coapContext[i], OLA_COAP_EVENT_SOCKET_SELECT_ERROR);
                }
            }
        }
    }
}

static int CoapMsgRecieve(OlaCoapContext *ctx, OlaCoapSession **session)
{
    (void)memset_s(ctx->recvBuf.buf, ctx->recvBuf.size, 0, ctx->recvBuf.size);
    ctx->recvBuf.len = 0;
    /* 使用公用接收缓冲区，同时预留'\0'位置 */
    OlaBuffer buf = {ctx->recvBuf.buf, 0, ctx->recvBuf.size - 1};
    int ret = CoapProtocolRecieve(ctx, session, &buf);
    ctx->recvBuf.len =  buf.len;
    return ret;
}

static int CoapMsgDecode(OlaCoapContext *ctx, OlaCoapPacket *pkt)
{
    OlaData msg = {ctx->recvBuf.buf, ctx->recvBuf.len};
    int ret = OlaCoapDecode(pkt, &msg, GetCoapFrameType(ctx));
    if (ret != OLA_OK) {
        OlaLogWarning("coap decode error");
        return ret;
    }

    return OLA_OK;
}

static int CoapUnreliableMsgCheck(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    /* udp报文要求msgid大于等于预期msgid且不超过30 */
    int ret = OLA_OK;
    if (session->lastRecvTime == 0) {
        return ret;
    }
    if (pkt->header.msgId < session->recvMsgId) {
        if (UINT16_MAX - session->recvMsgId + pkt->header.msgId + 1 > 30) {
            ret = OLA_COAP_ERR_INVALID_PACKET;
        }
    } else {
        if (pkt->header.msgId - session->recvMsgId > 30) {
            ret = OLA_COAP_ERR_INVALID_PACKET;
        }
    }
    if (ret != OLA_OK) {
        OlaLogWarning("coap msgid error except[%X] recv [%X]", session->recvMsgId, pkt->header.msgId);
    }
    return ret;
}

static int CoapReliableMsgCheck(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    /* 判断是否为重发报文 */
    OlaCoapRecvItem *item = CoapRecvListFindItemByPkt(session, pkt);
    if (item == NULL) {
        return OLA_OK;
    }
    return OLA_COAP_ERR_INVALID_PACKET;
}

static int CoapMsgCheck(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    int ret = OLA_COM_ERR_NOT_SUPPORT;
    switch (GetCoapFrameType(session->context)) {
        case OLA_COAP_FRAME_TYPE_RELIABLE:
            ret = CoapReliableMsgCheck(session, pkt);
            break;
        case OLA_COAP_FRAME_TYPE_UNRELIABLE:
            ret = CoapUnreliableMsgCheck(session, pkt);
            break;
        default:
            break;
    }

    return ret;
}

static OlaCoapPacketType GetCoapRecvMsgType(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    if (GetCoapFrameType(session->context) == OLA_COAP_FRAME_TYPE_UNRELIABLE) {
        if ((pkt->header.type == OLA_COAP_MSG_TYPE_CON) ||
            (pkt->header.code <= OLA_COAP_METHOD_TYPE_DELETE)) {
            return OLA_COAP_PACKET_TYPE_REQUEST;
        } else if (pkt->header.type == OLA_COAP_MSG_TYPE_ACK) {
            return OLA_COAP_PACKET_TYPE_RESPONSE;
        }
    } else {
        /* TODO 空包探测回复 */
        if (pkt->header.code == 0) {
            return OLA_COAP_PACKET_TYPE_EMPTY;
        } else if (pkt->header.code <= OLA_COAP_METHOD_TYPE_DELETE) {
            return OLA_COAP_PACKET_TYPE_REQUEST;
        } else {
            return OLA_COAP_PACKET_TYPE_RESPONSE;
        }
    }

    return OLA_COAP_ERR_UNKNOWN_MSG_TYPE;
}

static int CoapRsponseMsgDispatch(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    OlaCoapReqItem *req = CoapReqListFindItem(session, pkt);
    if (req == NULL) {
        OlaLogInfo("ingnore response");
        return OLA_OK;
    }
    if (req->handler != NULL) {
        req->handler(session, pkt);
    } else {
        OlaLogInfo("recieve response, but no handler");
        return OLA_OK;
    }

    /* 释放掉req以及resend链表中的资源 */
    CoapContextReqListReleaseItem(session->context, req);
    if (GetCoapFrameType(session->context) == OLA_COAP_FRAME_TYPE_UNRELIABLE) {
        OlaCoapResendItem *item = CoapResendListFindItem(session, pkt);
        if (item != NULL) {
            CoapContextResendListReleaseItem(session->context, item);
        }
    }
    return OLA_OK;
}

static OlaCoapRecvItem *CoapBuildRecvItem(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    OlaCoapRecvItem *item = (OlaCoapRecvItem *)OlaMalloc(sizeof(OlaCoapRecvItem));
    if (item == NULL) {
        OlaLogError("malloc Error");
        return NULL;
    }

    do {
        (void)memset_s(item, sizeof(OlaCoapRecvItem), 0, sizeof(OlaCoapRecvItem));
        item->msgId = pkt->header.msgId;
        item->tokenLen = pkt->header.tokenLen;
        if (pkt->header.tokenLen != 0) {
            if (memcpy_s(item->token, OLA_COAP_TOKEN_MAX_LEN, pkt->token.data, pkt->header.tokenLen) != EOK) {
                OlaLogError("memcpy Error");
                break;
            }
        }
        item->session = session;

        if (OlaGetTime(&item->recvTime) != OLA_OK) {
            OlaLogWarning("get time error");
            break;
        }

        return item;
    } while (0);

    OlaPutNullAfterFree((void **)&item);
    return NULL;
}

static int CoapBuildRespCoapPktParam(OlaCoapSession *session, const OlaCoapPacket *pkt, unsigned char code,
    const OlaData *payload, OlaCoapPacketParam *pktParam)
{
    pktParam->frameType = GetCoapFrameType(session->context);
    pktParam->msgType = OLA_COAP_MSG_TYPE_ACK;
    pktParam->code = code;
    pktParam->msgId = pkt->header.msgId;
    pktParam->optionsNum = 0;
    pktParam->tokenLen = pkt->header.tokenLen;
    if (pkt->header.tokenLen != 0) {
        if (memcpy_s(pktParam->token, OLA_COAP_TOKEN_MAX_LEN, pkt->token.data, pkt->header.tokenLen) != EOK) {
            OlaLogError("memcpy error");
            return OLA_SECUREC_ERR_MEMCOPY;
        }
    }
    pktParam->payload.data = payload->data;
    pktParam->payload.len = payload->len;
    return OLA_OK;
}

static int CoapRequestMsgSendResponse(OlaCoapSession *session, const OlaCoapPacket *recvPkt,
    unsigned char code, const OlaData *payload)
{
    OlaCoapPacketParam param;
    (void)memset_s(&param, sizeof(OlaCoapPacketParam), 0, sizeof(OlaCoapPacketParam));
    int ret = CoapBuildRespCoapPktParam(session, recvPkt, code, payload, &param);
    if (ret != OLA_OK) {
        OlaLogWarning("build coap param error");
        return ret;
    }

    ret = CoapSendDataInner(session, NULL, &param);
    if (ret != OLA_OK) {
        OlaLogWarning("send data error");
        return ret;
    }

    return  OLA_OK;
}

static void CoapRefreshSessionAfterRecvReq(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    session->recvMsgId = pkt->header.msgId + 1;
    if (OlaGetTime(&session->lastRecvTime) != 0) {
        OlaLogCrit("get time error");
        return;
    }
}

static int CoapRequestMsgDispatch(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    CoapRefreshSessionAfterRecvReq(session, pkt);
    unsigned char uriBuf[OLA_COAP_URI_MAX_LEN] = { 0 };
    OlaBuffer uri = {uriBuf, 0, OLA_COAP_URI_MAX_LEN};
    int ret;

    unsigned char code = OLA_COAP_RESPONSE_CODE_INTERNAL_ERROR;
    OlaData payload = {NULL, 0};
    do {
        ret = OlaCoapGetUri(pkt, &uri);
        if ((ret != OLA_OK) || uri.buf[OLA_COAP_URI_MAX_LEN - 1] != 0) {
            OlaLogWarning("get uri failed[%d]", ret);
            if (ret == OLA_COAP_ERR_OPTION_NO_URI) {
                code = OLA_COAP_RESPONSE_CODE_BAD_OPTION;
            }
            break;
        }
        OlaData uriData = {uri.buf, uri.len};
        OlaCoapResource *res = CoapContextFindResource(session->context, &uriData, pkt);
        if (res == NULL) {
            OlaLogWarning("not find target res [%s]", uri.buf);
            code = OLA_COAP_RESPONSE_CODE_BAD_OPTION;
            break;
        }

        OlaCoapRecvItem *recvItem = CoapBuildRecvItem(session, pkt);
        if (recvItem == NULL) {
            OlaLogWarning("build recv item error");
            break;
        }
        CoapContextRecvListAddItem(session->context, recvItem);

        ret = res->handler(session, pkt, &payload, (OlaReqId)recvItem);
        if ((ret == OLA_OK) || (ret == OLA_COAP_RESP_LAST_MSG)) {
            code = OLA_COAP_RESPONSE_CODE_CONTENT;
        }
        if (ret == OLA_COAP_RESP_LAST_MSG) {
            session->overFlag = true;
        }
    } while (0);

    if (ret != OLA_COAP_RESP_NOT_RESP) {
        ret = CoapRequestMsgSendResponse(session, pkt, code, &payload);
    } else {
        ret = OLA_OK;
    }

    OlaPutNullAfterFree((void **)&payload.data);
    if (ret != OLA_OK) {
        OlaLogWarning("send resp error");
        return ret;
    }
    return OLA_OK;
}

static int CoapMsgDispatch(OlaCoapSession *session, const OlaCoapPacket *pkt)
{
    int ret = OLA_COM_ERR_NOT_SUPPORT;
    switch (GetCoapRecvMsgType(session, pkt)) {
        case OLA_COAP_PACKET_TYPE_REQUEST:
            ret = CoapRequestMsgDispatch(session, pkt);
            break;
        case OLA_COAP_PACKET_TYPE_RESPONSE:
            ret = CoapRsponseMsgDispatch(session, pkt);
            break;
        default:
            break;
    }

    if (ret != OLA_OK) {
        OlaLogWarning("msg dispatch error");
        return ret;
    }

    return OLA_OK;
}

#ifdef OLA_COAP_DEBUG
static void CoapSessionDump(const OlaCoapSession *session)
{
    if (session == NULL) {
        return;
    }
    OlaPrintf("     session:\r\n");
    const OlaCoapSession *point = session;
    OlaSockaddrIn *fromAddr = NULL;
    while (point != NULL) {
        fromAddr = (OlaSockaddrIn *)&point->addr;
        OlaPrintf("               %s:%d\r\n", OlaInetNtoa(fromAddr->sinAddr), OlaNtohs(fromAddr->sinPort));
        point = point->next;
    }
}

static void CoapResendMsgDump(const OlaCoapResendItem *resendMsg)
{
    if (resendMsg == NULL) {
        return;
    }
    OlaPrintf("     resend: [type][msgid][sendcount]\r\n");
    const OlaCoapResendItem *point = resendMsg;
    while (point != NULL) {
        OlaPrintf("               [%d][%X][%u]\r\n", point->resendFlag,
            point->packet.header.msgId, point->sendCount);
        point = point->next;
    }
}

static void CoapResourceDump(const OlaCoapResource *resource)
{
    if (resource == NULL) {
        return;
    }
    OlaPrintf("     resource:\r\n");
    const OlaCoapResource *point = resource;
    while (point != NULL) {
        OlaPrintf("               %s\r\n", point->uri);
        point = point->next;
    }
}

static void CoapReqMsgDump(const OlaCoapReqItem *reqMsg)
{
    if (reqMsg == NULL) {
        return;
    }
    OlaPrintf("     req: [msgid][reqTime]\r\n");
    const OlaCoapReqItem *point = reqMsg;
    while (point != NULL) {
        OlaPrintf("               [%u][%lld]\r\n", point->msgId, point->reqTime);
        point = point->next;
    }
}

static void CoapRecvMsgDump(const OlaCoapRecvItem *recvMsg)
{
    if (recvMsg == NULL) {
        return;
    }
    OlaPrintf("     recv: [msgid][reqTime]\r\n");
    const OlaCoapRecvItem *point = recvMsg;
    while (point != NULL) {
        OlaPrintf("               [%u][%lld]\r\n", point->msgId, point->recvTime);
        point = point->next;
    }
}

static void CoapContextDump(const OlaCoapContext *ctx)
{
    unsigned int i;
    for (i = 0 ; i < OLA_COAP_CONTEXT_MAX_NUM; ++i) {
        if (ctx == &g_coapContext[i]) {
            break;
        }
    }
    if (i == OLA_COAP_CONTEXT_MAX_NUM) {
        return;
    }
    OlaPrintf("Coap Context:\r\n");
    OlaPrintf("     useflag   [%d]\r\n", ctx->useFlag);
    OlaPrintf("     errorflag [%d]\r\n", ctx->errFlag);
    OlaPrintf("     fd        [%d]\r\n", ctx->fd);
    OlaPrintf("     protocol  [%d]\r\n", ctx->protocol);
    OlaPrintf("     type      [%d]\r\n", ctx->type);
    OlaPrintf("     recvbuf   [%u]\r\n", ctx->recvBuf.size);
    if (GetCoapFrameType(ctx) == OLA_COAP_FRAME_TYPE_RELIABLE) {
        OlaPrintf("     sendbuf   [%u]\r\n", ctx->sendBuf.size);
    } else {
        OlaPrintf("     sendbuf   [%u]/[%u]\r\n", ctx->sendBuf.len, ctx->sendBuf.size);
    }
    CoapSessionDump(ctx->session);
    CoapResourceDump(ctx->resource);
    CoapResendMsgDump(ctx->resendMsg);
    CoapReqMsgDump(ctx->reqMsg);
    CoapRecvMsgDump(ctx->recvMsg);
}
#endif

static int CoapMsgProcess(OlaCoapSession *session)
{
    int ret;
    OlaCoapPacket pkt;
    (void)memset_s(&pkt, sizeof(OlaCoapPacket), 0, sizeof(OlaCoapPacket));
    ret = CoapMsgDecode(session->context, &pkt);
    if (ret != OLA_OK) {
        OlaLogWarning("decode msg error");
        return ret;
    }
    OlaCoapDumpPacket(&pkt);
    /* 校验 */
    ret = CoapMsgCheck(session, &pkt);
    if (ret != OLA_OK) {
        OlaLogWarning("invalid pkt");
        return ret;
    }

    /* 分发 */
    ret = CoapMsgDispatch(session, &pkt);
    if (ret != OLA_OK) {
        OlaLogWarning("dispatch msg error");
        return ret;
    }

    return OLA_OK;
}

static int CoapContextRecieveMsgProcess(OlaCoapContext *ctx)
{
    int ret;
    ret = OlaMutexLock(ctx->mutex, OLA_COAP_MUTEX_TIMEOUT);
    if (ret != 0) {
        OlaLogError("lock error[%d]", ret);
        return OLA_OS_ERR_MUTEX_LOCK;
    }

    do {
        OlaCoapSession *session = NULL;
        /* 读Coap包 */
        ret = CoapMsgRecieve(ctx, &session);
        if (ret != OLA_OK) {
            OlaLogWarning("recieve msg error[%d]", ret);
            ret = OLA_COAP_ERR_RECIEVE_MSG;
            break;
        }
        if (session == NULL) {
            break;
        }
        /* coap报文处理，出错不退出 */
        ret = CoapMsgProcess(session);
        if (ret != OLA_OK) {
            OlaLogWarning("msg process error[%d]", ret);
            ret = OLA_OK;
            break;
        }
    } while (0);

    (void)OlaMutexUnLock(ctx->mutex);
    return ret;
}

static void CoapContextDumpProcess(OlaCoapContext *ctx)
{
#ifdef OLA_COAP_DEBUG
    if (ctx->dumpTime == 0) {
        if (OlaGetTime(&ctx->dumpTime) != 0) {
            OlaLogError("get time error");
            return;
        }
    }
    bool isOvertime = false;
    if (OlaIsOvertime(&isOvertime, ctx->dumpTime, OLA_CONTEXT_DUMP_TIMEVAL) != OLA_OK) {
        OlaLogWarning("is over time error");
        return;
    }
    if (isOvertime) {
        CoapContextDump(ctx);
        if (OlaGetTime(&ctx->dumpTime) != 0) {
            OlaLogError("get time error");
            return;
        }
    }
    return;
#else
    (void)ctx;
#endif
}

static int CoapRecieveProcess(unsigned int timeout)
{
    int fd[OLA_COAP_CONTEXT_MAX_NUM] = {0};
    OlaCoapContext *ctx[OLA_COAP_CONTEXT_MAX_NUM] = {0};
    int index = -1;
    for (unsigned int i = 0; i < OLA_COAP_CONTEXT_MAX_NUM; ++i) {
        if (g_coapContext[i].useFlag && !g_coapContext[i].errFlag &&
            g_coapContext[i].fd >= 0) {
            ctx[++index] = &g_coapContext[i];
            fd[index] = g_coapContext[i].fd;
        }
    }
    if (index < 0) {
        return OLA_COAP_ERR_NO_VALID_CONTEXT;
    }
    OlaFdSet fdSet = { fd, index + 1 };
    int ret = OlaSelect(&fdSet, NULL, NULL, timeout);
    if (ret == 0) {
        return OLA_OK;
    } else if (ret < 0) {
        OlaLogWarning("select error");
        CoapContextSelectErrorProcess();
        return OLA_OK;
    }

    for (unsigned int i = 0; i < index + 1; ++i) {
        if (fd[i] == ctx[i]->fd) {
            ret = CoapContextRecieveMsgProcess(ctx[i]);
            if (ret != OLA_OK) {
                OlaLogWarning("read msg error");
                return ret;
            }
        }
    }

    return OLA_OK;
}

static int CoapSessionProcess(OlaCoapContext *ctx)
{
    if ((ctx->type != OLA_COAP_CONTEXT_TYPE_SERVER) || (ctx->session == NULL)) {
        return OLA_OK;
    }

    return CoapContextSessListCheck(ctx);
}

static int CoapResendProcess(OlaCoapContext *ctx)
{
    if ((GetCoapFrameType(ctx) != OLA_COAP_FRAME_TYPE_UNRELIABLE) ||
        (ctx->resendMsg == NULL)) {
        return OLA_OK;
    }

    return CoapContextResendListCheck(ctx);
}

static int CoapRecvProcess(OlaCoapContext *ctx)
{
    if (ctx->recvMsg == NULL) {
        return OLA_OK;
    }

    return CoapContextRecvListCheck(ctx);
}

static int CoapReqProcess(OlaCoapContext *ctx)
{
    if (ctx->reqMsg == NULL) {
        return OLA_OK;
    }

    return CoapContextReqListCheck(ctx);
}

static int CoapContextProcess(OlaCoapContext *ctx)
{
    if (OlaMutexLock(ctx->mutex, OLA_COAP_MUTEX_TIMEOUT) != 0) {
        OlaLogError("lock error");
        return OLA_OS_ERR_MUTEX_LOCK;
    }

    int ret;
    do {
        ret = CoapSessionProcess(ctx);
        if (ret != OLA_OK) {
            OlaLogWarning("session process error");
            break;
        }

        ret = CoapResendProcess(ctx);
        if (ret != OLA_OK) {
            OlaLogWarning("resend process error");
            break;
        }

        ret = CoapRecvProcess(ctx);
        if (ret != OLA_OK) {
            OlaLogWarning("recv process error");
            break;
        }

        ret = CoapReqProcess(ctx);
        if (ret != OLA_OK) {
            OlaLogWarning("req process error");
            break;
        }
        CoapContextDumpProcess(ctx);
    } while (0);

    (void)OlaMutexUnLock(ctx->mutex);
    return ret;
}

static int CoapAllContextProcess(void)
{
    int ret;
    OlaCoapContext *ctx = NULL;
    for (unsigned int i = 0 ; i < OLA_COAP_CONTEXT_MAX_NUM; ++i) {
        if (!g_coapContext[i].useFlag || g_coapContext[i].errFlag) {
            continue;
        }
        ctx = &g_coapContext[i];
        ret = CoapContextProcess(ctx);
        if (ret != OLA_OK) {
            OlaLogWarning("context process error");
            return ret;
        }
    }
    return OLA_OK;
}

int OlaCoapProcess(unsigned int timeout)
{
    /* 处理收包 */
    int ret = CoapRecieveProcess(timeout);
    if (ret != OLA_OK) {
        OlaLogWarning("recive msg process error");
        return ret;
    }

    /* 上下文维护 */
    ret = CoapAllContextProcess();
    if (ret != OLA_OK) {
        OlaLogWarning("context process error");
        return ret;
    }
    return OLA_OK;
}

bool OlaIsCoapCanExit(OlaCoapContext *ctx)
{
    int ret;
    ret = CoapContextInvalidCheck(ctx);
    if (ret != OLA_OK) {
        OlaLogWarning("invalid context");
        return false;
    }
    if ((ctx->resendMsg != NULL) || (ctx->reqMsg != NULL)) {
        return false;
    }
    return true;
}